/* scrn.c */

#include <stdlib.h>
#include <string.h>

#include "kernel.h"     /* Low level interfaces to OS */

#include "DeskLib:Error.h"
#include "DeskLib:KernelSWIs.h" /* Contains interfaces to common SWI's */
#include "DeskLib:Screen.h"     /* Includes stuff for reading current mode */
#include "DeskLib:SWI.h"        /* Contains a list of common SWI's */
#include "DeskLib:WimpSWIs.h"

#include "clock.h"
#include "scrn.h"

#ifndef SWI_OS_ReadDynamicArea
#define SWI_OS_ReadDynamicArea 0x5c
#define SWI_OS_ChangeDynamicArea 0x2a
#endif

#ifndef SWI_OS_RemoveCursors
#define SWI_OS_RemoveCursors 0x36
#define SWI_OS_RestoreCursors 0x37
#endif

/* Table of offsets from screen base address of each row, reading downwards */
int scrn_row_table[240];

/* Difference between single rows */
int scrn_offset;

/* Screen bank being worked on */
int scrn_work_bank;

/* Screen base addresses for each bank */
void *scrn_bank_base[2];

/* Desktop mode */
static int scrn_desk_mode;

/* Plotter functions */
scrn_plotter scrn_fast_plotter,scrn_slow_plotter;
scrn_clocker scrn_clock_plotter;

void scrn_cancel_banking(void)
{
  _kernel_swi_regs r;
  int vartags[] = {148, -1};

  OS_Byte(112,0,0,NULL,NULL);
  OS_Byte(113,0,0,NULL,NULL);

  r.r[0] = (int) vartags;
  r.r[1] = (int) &scrn_address;
  _kernel_swi(SWI_OS_ReadVduVariables,&r,&r);
  scrn_address2 = scrn_address;
}

void scrn_start_banking(void)
{
  _kernel_swi_regs r;
  int vartags[] = {148, 149, -1};

  scrn_work_bank = 1;
  _kernel_osbyte(112,2,0);
  _kernel_oswrch(12); /* Clear bank 2 */
  _kernel_osbyte(112,1,0);
  _kernel_oswrch(12); /* Clear bank 1 */
  _kernel_osbyte(113,2,0);
  r.r[0] = (int) vartags;
  r.r[1] = (int) scrn_bank_base;
  _kernel_swi(SWI_OS_ReadVduVariables,&r,&r);
  scrn_address = scrn_address2 = scrn_bank_base[0];
}

BOOL scrn_set_mode(int mode)
{
  _kernel_swi_regs r;

  if (Error_Check((os_error *) _kernel_swi(256 + 22,&r,&r)))
    return FALSE;
  return !((BOOL) Error_Check((os_error *) _kernel_swi(256 + mode,&r,&r)));
}

BOOL scrn_setup(scrn_type type)
{
  int row;
  int gamemode;
  int claimmem;
  _kernel_swi_regs r;
  int vartags[] = {148, -1};

  /* Make sure there is enough memory for both screen banks */
  if (type)
  {
    claimmem = 320 * 480 * 2;
    gamemode = 49;
  }
  else
  {
    claimmem = 320 * 256 * 2;
    gamemode = 13;
  }
  r.r[0] = 2;
  _kernel_swi(SWI_OS_ReadDynamicArea,&r,&r);
  if (r.r[1] < claimmem)
  {
    r.r[0] = 2;
    r.r[1] = claimmem - r.r[1];
    if (Error_Check((os_error *) _kernel_swi(SWI_OS_ChangeDynamicArea,&r,&r)))
      return FALSE;
  }

  if (type == scrn_double_vga)
  {
    scrn_slow_plotter = scrn_plot_vga;
    scrn_fast_plotter = scrn_fast_plot_vga;
    scrn_clock_plotter = clock_draw_double;
  }
  else
  {
    scrn_slow_plotter = scrn_plot;
    scrn_fast_plotter = scrn_fast_plot;
    scrn_clock_plotter = clock_draw;
  }

  /* Set mode */
  if (!scrn_set_mode(gamemode))
    return FALSE;

  /* Set up table */
  OS_ReadModeVariable(-1,6,&scrn_rowsize);
  if (type)
  {
    scrn_offset = scrn_rowsize;
    scrn_rowsize <<= 1;
  }
  scrn_rowsize2 = scrn_rowsize;
  for (row = 0; row < 240; row++)
    scrn_row_table[row] = row * scrn_rowsize;
  scrn_table_ptr = scrn_table_ptr2 = scrn_row_table;

  r.r[0] = (int) vartags;
  r.r[1] = (int) &scrn_address;
  _kernel_swi(SWI_OS_ReadVduVariables,&r,&r);
  scrn_address2 = scrn_address;

  return TRUE;
}

void scrn_swap_banks(void)
{
  scrn_wait();
  OS_Byte(113,scrn_work_bank,0,NULL,NULL);
  scrn_work_bank ^= 3; /* Switches between 1 & 2 */
  OS_Byte(112,scrn_work_bank,0,NULL,NULL);
  scrn_address = scrn_address2 = scrn_bank_base[scrn_work_bank - 1];
}

void scrn_cursor_off()
{
  _kernel_swi_regs r;

  _kernel_swi(SWI_OS_RemoveCursors,&r,&r);
}

void scrn_cursor_on()
{
  _kernel_swi_regs r;

  _kernel_swi(SWI_OS_RestoreCursors,&r,&r);
}

void scrn_save_desktop_mode()
{
  Screen_CacheModeInfo();
  if (screen_mode > 255) /* RISC PC mode selector */
  {
    int index;
    int size;

    /* Find number of variable pairs */
    for (index = 5; ((int *) screen_mode)[index] != -1; index += 2);
    /* Allocate memory for this */
    size = (index + 1) * 4;
    scrn_desk_mode = (int) malloc(size);
    /* Copy data to our block */
    memcpy((void *) scrn_desk_mode,(void *) screen_mode,size);
  }
  else /* simple old-fangled mode number */
    scrn_desk_mode = screen_mode;
}

void scrn_restore_desktop_mode()
{
  Error_Check(Wimp_SetMode(scrn_desk_mode));
  if (scrn_desk_mode > 255)
    free((void *) scrn_desk_mode);
}
